import 'dart:convert';
import 'dart:io';

import 'package:cross_file/cross_file.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_encode/utils/util.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'X-Tiao加密解密工具',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const FileEncoderHomePage(title: 'X-Tiao加密解密工具'),
    );
  }
}

class EncodeDirectoryOrFile {
  // 是否是文件夹
  bool isDirectoryValue = false;

  set isDirectory(bool isDirectory) {
    isDirectoryValue = isDirectory;
    if (isDirectory) {
      children = <EncodeDirectoryOrFile>[];
    } else {
      children = null;
    }
  }

  bool get isDirectory => isDirectoryValue;

  // 名称
  String? name;

  // 如果是文件夹，那么就是文件夹的内容
  List<EncodeDirectoryOrFile>? children;

  // 如果是文件，那么就是文件的内容
  String? fileContent;

  // 文件类型
  String? fileType;

  // 文件大小
  String? fileSize;

  void fromJson(Map<String, dynamic> json) {
    isDirectory = json['isDirectory'];
    name = json['name'];
    if (isDirectory) {
      List<dynamic> childrenFromJson = json['children'];
      children = <EncodeDirectoryOrFile>[];
      for (var element in childrenFromJson) {
        EncodeDirectoryOrFile encodeDirectoryOrFile = EncodeDirectoryOrFile();
        encodeDirectoryOrFile.fromJson(element);
        children!.add(encodeDirectoryOrFile);
      }
    } else {
      fileContent = json['fileContent'];
      fileType = json['fileType'];
      fileSize = json['fileSize'];
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['isDirectory'] = isDirectory;
    data['name'] = name;
    if (isDirectory) {
      data['children'] = children;
    } else {
      data['fileContent'] = fileContent;
      data['fileType'] = fileType;
      data['fileSize'] = fileSize;
    }
    return data;
  }
}

/// 首页所有的状态管理Model
class HomeViewModel extends ChangeNotifier {
  // 需要加密的文件夹
  XFile? needEncodeDirectory;

  // 需要解密的文件
  XFile? needDecodeFile;

  // 是否正在处理
  bool isProcessingValue = false;

  set isProcessing(bool isProcessing) {
    isProcessingValue = isProcessing;
    notifyListeners();
  }

  bool get isProcessing => isProcessingValue;

  // 当前是否是加密模式
  bool isEncodeMode = true;
  Logger logger = Logger(
    printer: PrettyPrinter(
      methodCount: 1,
      errorMethodCount: 8,
      lineLength: 120,
      colors: true,
      printEmojis: true,
      printTime: true,
    ),
  );

  void setNeedEncodeDirectory(XFile? file, Directory ioDirectory) {
    // 打印文件夹名称、数量、地址
    logger.i("文件夹名称:${file?.name}");
    logger.i("文件数量:${ioDirectory.listSync().length}");
    logger.i("文件夹地址:${file?.path}");
    needEncodeDirectory = file;
    notifyListeners();
  }

  void onReceivedEncodeDirectory(DropDoneDetails detail) {
    // XFile判断是否是文件夹
    XFile file = detail.files.first;
    File ioFile = File(file.path);
    if (ioFile.statSync().type == FileSystemEntityType.directory) {
      Directory directory = Directory(file.path ?? "");
      setNeedEncodeDirectory(file, directory);
    } else {
      logger.i("不是文件夹,不处理");
    }
  }

  void setNeedDecodeFile(XFile? file) {
    needDecodeFile = file;
    notifyListeners();
  }

  void onReceivedDecodeFile(DropDoneDetails detail) {
    // XFile判断是否是文件
    XFile file = detail.files.first;
    File ioFile = File(file.path);
    if (ioFile.statSync().type == FileSystemEntityType.file) {
      setNeedDecodeFile(file);
    } else {
      logger.i("不是文件,不处理");
    }
  }

  void onSwitchMode() {
    if (isProcessing) {
      return;
    }
    isEncodeMode = !isEncodeMode;
    notifyListeners();
  }

  void onEncode(BuildContext context) {
    if (isProcessing) {
      return;
    }
    logger.i("开始加密");
    // 是否有文件夹
    bool hasDirectory = needEncodeDirectory != null;
    if (!hasDirectory) {
      "请拖动文件到框内再点击开始加密".errorToast(context);
      logger.w("没有文件夹,不处理");
      return;
    }
    isProcessing = true;
    try {
      // 读取文件夹的名称和文件数量，准备合并成一个文件
      String directoryName = needEncodeDirectory?.name ?? "";
      Directory directory = Directory(needEncodeDirectory?.path ?? "");
      // 判断是否存在
      if (!directory.existsSync()) {
        "文件夹不存在".errorToast(context);
        logger.w("文件夹不存在,不处理");
        return;
      }
      int fileCount = directory.listSync().length;
      EncodeDirectoryOrFile encodeDirectory = EncodeDirectoryOrFile();
      encodeDirectory.isDirectory = true;
      logger.i("文件夹名称:$directoryName");
      logger.i("文件数量:$fileCount");
      encodeDirectory.name = directoryName;
      // 递归处理整个文件夹
      whenEncodeSolveDirectory(directory, encodeDirectory);
      // 将加密文件信息写入到文件中,在文件夹同级目录生成一个文件，文件名称为tmp+当前时间戳加密后
      File tmpFile = File("${directory.parent.path}/${generateEncodeFileName()}");
      String resultContent = jsonEncode(encodeDirectory.toJson());
      // Base64加密
      resultContent = resultContent.base64Encode();
      tmpFile.writeAsStringSync(resultContent);
      "加密完成".successToast(context);
    }catch (e) {
      "加密失败".errorToast(context);
      logger.e("加密失败:$e");
    }finally {
      isProcessing = false;
    }
  }

  /// 生成加密后的文件名称 文件名称为tmp+当前时间戳Base64加密后
  String generateEncodeFileName() {
    String currentTime = DateTime.now().millisecondsSinceEpoch.toString();
    // 进行Base64加密
    String encodeName = currentTime.base64Encode();
    String tmpFileName = "tmp$encodeName";
    logger.i("生成的文件名称:$tmpFileName");
    return tmpFileName;
  }

  /// 加密时处理文件
  void whenEncodeSolveFile(File file, EncodeDirectoryOrFile encodeDirectory) {
    String fileName = file.path.split(Platform.pathSeparator).last;
    String fileType = fileName.split(".").last;
    String fileSize = file.lengthSync().toString();
    String fileContent = file.readAsBytesSync().base64Encode();
    logger.i("文件名称:$fileName");
    logger.i("文件类型:$fileType");
    logger.i("文件大小:$fileSize");
    EncodeDirectoryOrFile encodeFile = EncodeDirectoryOrFile();
    encodeFile.isDirectory = false;
    encodeFile.name = fileName;
    encodeFile.fileType = fileType;
    encodeFile.fileSize = fileSize;
    encodeFile.fileContent = fileContent;
    encodeDirectory.children?.add(encodeFile);
  }

  /// 加密时处理文件夹
  void whenEncodeSolveDirectory(
      Directory directory, EncodeDirectoryOrFile encodeDirectory) {
    // 读取文件夹下的所有文件
    List<File> files = directory.listSync().map((e) => File(e.path)).toList();
    // 读取文件夹下的所有文件的名称并打印
    for (File file in files) {
      // 判断是否是文件夹，如果是文件夹则递归调用，如果不是文件夹则直接加密
      if (file.statSync().type == FileSystemEntityType.directory) {
        // 是文件夹
        Directory childIODirectory = Directory(file.path);
        EncodeDirectoryOrFile childDirectory = EncodeDirectoryOrFile();
        childDirectory.isDirectory = true;
        childDirectory.name = childIODirectory.path.split(Platform.pathSeparator).last;
        encodeDirectory.children?.add(childDirectory);
        whenEncodeSolveDirectory(childIODirectory, childDirectory);
      } else {
        // 不是文件夹
        whenEncodeSolveFile(file, encodeDirectory);
      }
    }
  }

  /// 递归调用解密文件夹
  void decodeDirectory(
      bool isRoot, String currentPath, EncodeDirectoryOrFile currentDirectory) {
    // 生成文件夹
    // 兼容Windows和Mac
    String directoryPath =
        isRoot ? currentPath : "$currentPath${Platform.pathSeparator}${currentDirectory.name}";
    Directory directory = Directory(directoryPath);
    directory.createSync();
    // 生成文件
    for (EncodeDirectoryOrFile encodeFile in currentDirectory.children ?? []) {
      if (encodeFile.isDirectory) {
        // 是文件夹
        decodeDirectory(false, directory.path, encodeFile);
      } else {
        // 不是文件夹
        File file = File("${directory.path}${Platform.pathSeparator}${encodeFile.name}");
        List<int> bytes = encodeFile.fileContent?.base64DecodeToBytes() ?? [];
        file.writeAsBytesSync(bytes);
      }
    }
  }

  void onDecode(BuildContext context) {
    if (isProcessing) {
      return;
    }
    logger.i("开始解密");
    isProcessing = true;
    // 是否有文件
    bool hasFile = needDecodeFile != null;
    if (!hasFile) {
      "请拖动文件到框内再点击开始解密".errorToast(context);
      logger.w("没有文件,不处理");
      isProcessing = false;
      return;
    }
    String filePath = needDecodeFile?.path ?? "";
    File file = File(filePath);
    // 判断是否存在
    if (!file.existsSync()) {
      "文件不存在".errorToast(context);
      logger.w("文件不存在,不处理");
      isProcessing = false;
      return;
    }
    try {
      String fileContent = file.readAsStringSync();
      // Base64解密
      fileContent = fileContent.base64Decode();
      // 解析json
      Map<String, dynamic> json = jsonDecode(fileContent);
      EncodeDirectoryOrFile encodeDirectory = EncodeDirectoryOrFile();
      encodeDirectory.fromJson(json);
      // 当前时间戳
      String currentTime = DateTime.now().millisecondsSinceEpoch.toString();
      // 生成文件夹在解密文件同级目录下
      // 递归调用解密文件夹
      decodeDirectory(
          true,
          "${file.parent.path}${Platform.pathSeparator}${encodeDirectory.name != null ? "${encodeDirectory.name}-$currentTime" : '无文件夹名称自动生成$currentTime'}",
          encodeDirectory);
      "解密完成".successToast(context);
      logger.i("解密完成");
    } catch (e) {
      "解密失败，请确认是否是加密文件或加密文件受损".errorToast(context);
      logger.e("解密失败", e);
      return;
    } finally {
      isProcessing = false;
    }
  }
}

class DirectoryDragTarget extends StatefulWidget {
  const DirectoryDragTarget({Key? key}) : super(key: key);

  @override
  _DirectoryDragTargetState createState() => _DirectoryDragTargetState();
}

class _DirectoryDragTargetState extends State<DirectoryDragTarget> {
  bool _dragging = false;

  @override
  Widget build(BuildContext context) {
    // ViewModel
    return Consumer<HomeViewModel>(builder: (context, homeViewModel, child) {
      return DropTarget(
        onDragDone: (detail) {
          // XFile判断是否是文件夹
          if (homeViewModel.isEncodeMode) {
            homeViewModel.onReceivedEncodeDirectory(detail);
          } else {
            homeViewModel.onReceivedDecodeFile(detail);
          }
        },
        onDragEntered: (detail) {
          setState(() {
            _dragging = true;
          });
        },
        onDragExited: (detail) {
          setState(() {
            _dragging = false;
          });
        },
        child: Container(
          height: 200,
          width: 200,
          color: _dragging ? Colors.blue.withOpacity(0.4) : Colors.black26,
          child: (homeViewModel.isEncodeMode
                  ? homeViewModel.needEncodeDirectory == null
                  : homeViewModel.needDecodeFile == null)
              ? Center(
                  child: Text(homeViewModel.isEncodeMode
                      ? "拖动需要加密的文件夹到此处"
                      : "拖动需要解密的文件到此处"))
              : Text(homeViewModel.isEncodeMode
                  ? homeViewModel.needEncodeDirectory?.path ?? ""
                  : homeViewModel.needDecodeFile?.path ?? ""),
        ),
      );
    });
  }
}

class FileEncoderHomePage extends StatefulWidget {
  const FileEncoderHomePage({super.key, required this.title});

  final String title;

  @override
  State<FileEncoderHomePage> createState() => _FileEncoderHomePageState();
}

class _FileEncoderHomePageState extends State<FileEncoderHomePage> {
  HomeViewModel homeViewModel = HomeViewModel();

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
        value: homeViewModel,
        child: Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Container(
            // 15px边距
            margin: const EdgeInsets.all(15),
            child: Consumer(
                builder: (context, HomeViewModel homeViewModel, child) {
              return Column(
                // 居左
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // 选择加密模式 切换
                  Row(
                    children: [
                      const Text("选择模式"),
                      const SizedBox(width: 10),
                      // 两个按钮
                      Row(
                        children: [
                          // 加密模式
                          ElevatedButton(
                            onPressed: () {
                              homeViewModel.onSwitchMode();
                            },
                            style: ElevatedButton.styleFrom(
                                primary: homeViewModel.isEncodeMode
                                    ? Colors.blue
                                    : Colors.grey),
                            child: const Text("加密"),
                          ),
                          const SizedBox(width: 10),
                          // 解密模式
                          ElevatedButton(
                            onPressed: () {
                              homeViewModel.onSwitchMode();
                            },
                            style: ElevatedButton.styleFrom(
                                primary: homeViewModel.isEncodeMode
                                    ? Colors.grey
                                    : Colors.blue),
                            child: const Text("解密"),
                          ),
                        ],
                      ),
                    ],
                  ),
                  const SizedBox(height: 10),
                  // 选择文件夹，标题
                  Text(homeViewModel.isEncodeMode ? "选择需要加密的文件夹" : "选择需要解密的文件夹",
                      style: const TextStyle(
                          fontSize: 17,
                          fontWeight: FontWeight.w700,
                          fontStyle: FontStyle.normal)),
                  // 白色卡片,10px边距
                  const Card(
                    child: DirectoryDragTarget(),
                  ),
                  const SizedBox(height: 10),
                  // ElevatedButton(
                  //   onPressed: () {
                  //     if (homeViewModel.isEncodeMode) {
                  //       homeViewModel.onEncode(context);
                  //     } else {
                  //       homeViewModel.onDecode(context);
                  //     }
                  //   },
                  //   child: Text(homeViewModel.isEncodeMode ? "开始加密" : "开始解密"),
                  // ),
                  // 加密解密按钮 当处理中时变成加载动态图标并变灰色按钮
                  ElevatedButton(
                    onPressed: homeViewModel.isProcessing
                        ? null
                        : () {
                            if (homeViewModel.isEncodeMode) {
                              homeViewModel.onEncode(context);
                            } else {
                              homeViewModel.onDecode(context);
                            }
                          },
                    child: homeViewModel.isProcessing
                        ? Container(
                            padding: const EdgeInsets.all(5),
                            child: const SizedBox(
                              width: 20,
                              height: 20,
                              child: CircularProgressIndicator(
                                strokeWidth: 2,
                              ),
                            ),
                          )
                        : Text(homeViewModel.isEncodeMode ? "开始加密" : "开始解密"),
                  ),
                ],
              );
            }),
          ),
        ));
  }
}
